package com.fresh.bus.chat.business;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.BulkMapper;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.query.SortQuery;
import org.springframework.data.redis.core.query.SortQueryBuilder;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.data.redis.support.collections.DefaultRedisList;
import org.springframework.data.redis.support.collections.DefaultRedisMap;
import org.springframework.data.redis.support.collections.DefaultRedisSet;
import org.springframework.data.redis.support.collections.DefaultRedisZSet;
import org.springframework.data.redis.support.collections.RedisList;
import org.springframework.data.redis.support.collections.RedisMap;
import org.springframework.data.redis.support.collections.RedisSet;
import org.springframework.data.redis.support.collections.RedisZSet;
import org.springframework.stereotype.Component;

import com.fresh.bus.canoe.message.Response;
import com.fresh.bus.canoe.session.Session;
import com.fresh.bus.canoe.session.SessionRoleManager;
import com.fresh.bus.chat.ctrl.ChatCtrl;
import com.fresh.bus.chat.ctrl.OpCodes;
import com.fresh.bus.chat.domain.User;
import com.fresh.bus.chat.persist.SystemDao;
import com.fresh.bus.chat.persist.UserDao;
import com.fresh.bus.chat.util.DateUtil;
import com.fresh.bus.chat.util.MessageUtils;

public class UserService implements InitializingBean {
	private static final Logger log = LoggerFactory
			.getLogger(UserService.class);

	public static final short DAILY_WORD_AMT = 3;
	public static final short DAILY_QQNO_AMT = 5;
	public static final short DAILY_FEIJI_AMT = 1;
	public static final short MAX_FRIEND_AMT = 50;
	public static final short DEFAULT_FRIEND_VALUE = 15;

	private final StringRedisTemplate template;
	private final RedisAtomicLong commentIdCounter;

	@Resource
	private ScheduleManager scheduleManager;
	@Resource
	protected SessionRoleManager roleManager;
	@Resource
	private ExperienceService expService;
	@Resource
	private WealthService wealthService;
	@Resource
	private LoginService loginService;
	@Resource
	private ZhiFeiJiService feijiService;
	@Resource
	private TreeService treeService;
	@Resource
	private MessageService msgService;

	@Resource
	private UserDao dao;

	@Resource
	private SystemConfig config;

	// global users
	private Set<String> synUserSet;

	@Resource
	protected ChatCtrl chatCtrl;

	@Autowired
	public UserService(StringRedisTemplate template) {
		this.template = template;
		synUserSet = new HashSet<String>();

		commentIdCounter = new RedisAtomicLong(KeyUtils.globalMid(),
				template.getConnectionFactory());
	}

	public void afterPropertiesSet() throws Exception {
		List<Map<String, Object>> males = dao.getLatestUsers("male");
		initUsers(males);
		List<Map<String, Object>> females = dao.getLatestUsers("female");
		initUsers(females);
	}
	
	public RedisZSet<String> areaGroup(String province,String sex) {
		return new DefaultRedisZSet<String>(KeyUtils.areaGroup(province,sex), template);
	}

	private void initUsers(List<Map<String, Object>> users) {
		Map<String, String> map;
		String id=null,province=null,sex=null;
		for (Map<String, Object> u : users) {
			map = readUser(u);
			id = map.get("id");
			province = getProvince(map);
			sex = map.get("sex");
			
			double score = countUserScore(map);
			this.areaGroup(province, sex).add(id, score);
		}
	}
	
	public String getProvince(Map<String, String> user) {
		String rid = user.get("location");
		String[] loc = rid.split("\\-");

		if (loc.length > 1) {
			rid = loc[0];
		}
		return rid;
	}
	
	public double countUserScore(Map<String,String> user){
		Date latestVisite = DateUtil.convertStringToDate("yyyy-MM-dd",user.get("latestVisit"));
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(latestVisite);
		int day = calendar.get(Calendar.DAY_OF_WEEK);
		calendar.add(Calendar.WEEK_OF_YEAR, -1);
		calendar.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
		
		long loginTime = calendar.getTime().getTime();
		double score = loginTime / (1000 * 60);
		int level = Integer.valueOf(user.get("level"));
		score += level*1.2;
		score += day;
		return score;
	}

	public void init() {
		this.feijiService.initZhiFeiJis();
		this.feijiService.initStarFeijis();
		this.treeService.initTrees();
	}

	public Map<String, String> report(String uid) {
		BoundHashOperations<String, String, String> userOps = template
				.boundHashOps(KeyUtils.report(uid));
		return userOps.entries();
	}

	private RedisList<String> comments(String uid) {
		return new DefaultRedisList<String>(KeyUtils.comments(uid), template);
	}

	public Map<String, Object> readUser(String uid) {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("user", this.findUser(uid));
		map.put("report", this.findReport(uid));
		return map;
	}

	public void updateUserInfo(Map<String, String> map) throws Exception {
		String id = map.get("id");
		String selfDesc = map.get("selfDesc");
		BoundHashOperations<String, Object, Object> o = template
				.boundHashOps(KeyUtils.uid(id));
		o.put("selfDesc", selfDesc);

		// dao.updateUserInfo(selfDesc, loginId);
	}

	public List<Map<String, String>> queryUserComments(String uid) {
		String key = KeyUtils.comments(uid);
		String pid = "mid:*->";
		final String pidKey = "#";
		final String content = "content";
		final String time = "time";

		SortQuery<String> query = SortQueryBuilder.sort(key)
				.by(pidKey + " DESC").get(pidKey).get(pid + content)
				.get(pid + time).limit(0, -1).build();
		BulkMapper<Map<String, String>, String> hm = new BulkMapper<Map<String, String>, String>() {
			@Override
			public Map<String, String> mapBulk(List<String> bulk) {
				Map<String, String> map = new LinkedHashMap<String, String>();
				Iterator<String> iterator = bulk.iterator();

				String mid = iterator.next();
				map.put(content, iterator.next());
				map.put(time, iterator.next());

				return map;
			}
		};
		List<Map<String, String>> sort = template.sort(query, hm);
		return sort;
	}

	public List<Map<String, String>> queryUserDates(String uid) {
		String key = KeyUtils.comments(uid);

		String pid = "mid:*->";
		final String pidKey = "#";
		final String content = "content";
		final String time = "time";

		SortQuery<String> query = SortQueryBuilder.sort(key)
				.by(pidKey + " DESC").get(pidKey).get(pid + content)
				.get(pid + time).limit(0, -1).build();
		BulkMapper<Map<String, String>, String> hm = new BulkMapper<Map<String, String>, String>() {
			@Override
			public Map<String, String> mapBulk(List<String> bulk) {
				Map<String, String> map = new LinkedHashMap<String, String>();
				Iterator<String> iterator = bulk.iterator();

				String mid = iterator.next();
				map.put(content, iterator.next());
				map.put(time, iterator.next());

				return map;
			}
		};
		List<Map<String, String>> sort = template.sort(query, hm);

		return sort;
	}

	@SuppressWarnings("unchecked")
	public void evaluteUser(Map<String, String> map, String to, String from)
			throws Exception {
		boolean isNew = false;
		Map<String, Object> report = dao.getUserReport(to);
		if (report == null) {
			isNew = true;
			report = new HashMap<String, Object>();
		}
		for (String dataKey : map.keySet()) {
			Object value = report.get(dataKey);
			if (value != null) {
				value = (Integer) value + 1;
			} else {
				value = new Integer("1");
			}
			report.put(dataKey, value);
		}

		if (isNew) {
			dao.addUserReport(report, to);
		} else {
			dao.updateUserReport(report, to);
		}
		createReportInMem(report, to);

	}

	// 内存中创建属性报告
	private void createReportInMem(Map<String, Object> report, String id) {
		if (report == null)
			return;
		String rpid = KeyUtils.report(id);
		BoundHashOperations<String, String, String> rops = template
				.boundHashOps(rpid);
		for (String dataKey : report.keySet()) {
			Object value = report.get(dataKey);
			rops.put(dataKey, String.valueOf(value));
		}
	}

	public void commentUser(Map<String, Object> map, String to, String from)
			throws Exception {
		String comment = (String) map.get("content");
		if (comment != null && comment.trim().length() > 0) {
			String mid = String.valueOf(commentIdCounter.incrementAndGet());
			Date now = new Date();
			dao.addComment(comment, to, from, now);
			createCommentInMem(map, mid, now);
			this.comments(to).add(mid);
		}
	}

	public Map<String, String> findUser(String id) {
		String uid = KeyUtils.uid(id);
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(uid);
		Map<String, String> map = ops.entries();
		if (map.isEmpty()) {
			log.warn("read user: "+id+" failed,reload from database again");
			map = dao.getUserById(id);
			createUserInMem(map);
		}
		return map;
	}

	public Map<String, String> findReport(String id) {
		String rid = KeyUtils.report(id);
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(rid);
		return ops.entries();
	}

	public void synchroUser(Session session) {
		if(session!=null){
			String id = session.getRole().getId();
			String uid = KeyUtils.uid(id);
			Map<?, ?> map = template.boundHashOps(uid).entries();
			Response res = new Response(OpCodes.SYN_USER);
			res.setData(map);
			session.send(res);
		}
	}

	private List<String> covertUidsToNames(String key) {
		return template.sort(SortQueryBuilder.sort(key).noSort()
				.get("uid:*->name").build());
	}

	public Map<String, String> readUser(Map<String, Object> map) {
		Map<String, String> user = dao.processUserData(map);
		String login = (String) map.get("loginId");
		loginService.setUid(login, user.get("id"));

		String id = user.get("id");
		createUserInMem(user);
		Map<String, Object> report = dao.getUserReport(id);
		createReportInMem(report, id);
		readComment(id);

		return user;
	}

	@SuppressWarnings("unchecked")
	private void readComment(String id) {
		// 读取评论列表
		List<Map<String, Object>> coms = dao.getUserComment(id);
		RedisList<String> redisPosts = this.comments(id);
		for (Map<String, Object> c : coms) {
			String mid = String.valueOf(commentIdCounter.incrementAndGet());
			Date date = (Date) c.get("created_at");
			createCommentInMem(c, mid, date);
			redisPosts.add(mid);
		}
	}

	// 内存中创建评论
	private void createCommentInMem(Map<String, Object> map, String mid,
			Date now) {
		String pid = KeyUtils.mid(mid);
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(pid);
		ops.put("content", String.valueOf(map.get("content")));
		ops.put("time", String.valueOf(DateUtil.convertDateToString(now)));
	}

	// 内存中创建
	private void createUserInMem(Map<String, String> map) {
		String id = map.get("id");
		log.info("It is initlize user:[" + id + "] in memory...");

		String uid = KeyUtils.uid(id);
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(uid);
		ops.putAll(map);

		int level = Integer.valueOf(ops.get("level"));
		Map<String, Object> cond = config.findLevel(level + 1);
		int needExp = (Integer) cond.get("exp");
		int needIncome = (Integer) cond.get("income");
		int needPayout = (Integer) cond.get("payout");

		String sid = map.get("stop");
		String stopName = scheduleManager.stop(sid).get("name");

		ops.put("needExp", String.valueOf(needExp));
		ops.put("needIncome", String.valueOf(needIncome));
		ops.put("needPayout", String.valueOf(needPayout));
		ops.put("stopName", stopName);
	}

	public String clear(String id) {
		loginService.removeUid(id);

		BoundHashOperations<String, Object, Object> o = template
				.boundHashOps(KeyUtils.uid(id));
		String login = (String) o.get("loginId");
		// 删除用户相关的key
		List<String> keys = new ArrayList<String>();
		keys.add(KeyUtils.uid(id));
		keys.add(KeyUtils.report(id));
		keys.add(KeyUtils.comments(id));
		keys.add(KeyUtils.fans(id));
		keys.add(KeyUtils.feijis(id));

		template.delete(keys);
		return login;
	}

	public void setUserAddress(Session session, String sid) {
		String uid = session.getRole().getId();
		BoundHashOperations<String, Object, Object> o = template
				.boundHashOps(KeyUtils.uid(uid));
		o.put("stop", sid);
		String stopName = scheduleManager.stop(sid).get("name");
		o.put("stopName", stopName);
		synchroUser(session);
	}

	public void logoutUser(Map<String, String> map) {
		try {
			String uid = map.get("id");
			saveUser(uid);
			dao.logoutUser(map);
		} catch (Exception ex) {
			log.error("Save user failed:" + map.get("id"), ex);
		}
	}

	public void saveUser(String userId) {
		this.synUserSet.add(userId);
	}

	public void synUsers() {
		List<Map<String, String>> list = new ArrayList<Map<String, String>>();
		Map<String, String> data;
		Set<String> delSet = new HashSet<String>();
		for (Iterator<String> it = synUserSet.iterator(); it.hasNext();) {
			String id = it.next();
			Map<String, String> map = this.findUser(id);
			try {
				delSet.add(id);
				data = dao.tidyData(map);
				list.add(data);
			} catch (Exception e) {
				log.error("tidy data failed for user:" + id, e);
				continue;
			}
		}
		synUserSet.removeAll(delSet);

		if (!list.isEmpty()) {
			dao.batchUpdate(list);
		}
	}

	public boolean upgrade(Session session, Map<String, String> data)
			throws Exception {
		String id = session.getRole().getId();
		String uid = KeyUtils.uid(id);
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(uid);
		int level = Integer.valueOf(ops.get("level"));
		int exp = Integer.valueOf(ops.get("exp"));
		int dexp = Integer.valueOf(ops.get("dailyExp"));
		int income = Integer.valueOf(ops.get("income"));

		int needExp = Integer.valueOf(ops.get("needExp"));
		int needIncome = Integer.valueOf(ops.get("needIncome"));

		String recomUserId = ops.get("recom");

		if (dexp + exp >= needExp && income >= needIncome) {
			setUserData(ops, data);
			exp = exp - needExp;
			ops.put("exp", String.valueOf(exp));
			income = income - needIncome;
			ops.put("income", String.valueOf(income));
			level++;
			ops.put("level", String.valueOf(level));
			Map<String, Object> cond = config.findLevel(level);
			needExp = (Integer) cond.get("exp");
			needIncome = (Integer) cond.get("income");

			ops.put("needExp", String.valueOf(needExp));
			ops.put("needIncome", String.valueOf(needIncome));
			this.saveUser(id);

			loginService.synLatestUserList(ops.entries());
			// 回馈推荐人红豆
			if (recomUserId.trim().length() > 0 && level <= 5) {
				//wealthService.getFlower(recomUserId, level);
			}
			return true;
		} else {
			return false;
		}
	}

	/* 0-注册 1-纸飞机 2-种树 3-浇水  */
	public void forwardGuid(Session session,int index) {
		String id = session.getRole().getId();
		String uid = KeyUtils.uid(id);
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(uid);
		String step = ops.get("guideStep");
		step = MessageUtils.replace(step, index, "1");
		ops.put("guideStep", step);
		this.saveUser(id);
		this.synchroUser(session);
	}

	private void setUserData(BoundHashOperations<String, String, String> ops,
			Map<String, String> data) {
		int level = Integer.valueOf(data.get("level"));
		switch (level) {
		case 0:
			ops.put("location", data.get("data1"));
			ops.put("birthYear", data.get("data2"));
			ops.put("tall", data.get("data4"));
			ops.put("selfDesc", data.get("desc"));
			ops.put("degree", data.get("data5"));
			ops.put("marriage", data.get("data6"));
			break;
		case 1:
			ops.put("birthLocation", data.get("data1"));
			ops.put("xingzuo", data.get("data2"));
			ops.put("shuxiang", data.get("data3"));
			break;
		case 2:
			ops.put("job", data.get("data1"));
			ops.put("company", data.get("data2"));
			ops.put("weight", data.get("data3"));
			break;
		case 4:
			ops.put("gouche", data.get("data1"));
			ops.put("goufang", data.get("data2"));
			ops.put("nianxin", data.get("data3"));
			break;
		case 5:
			ops.put("xiyan", data.get("data1"));
			ops.put("yinjiu", data.get("data2"));
			ops.put("zuoxi", data.get("data3"));
			break;
		case 6:
			ops.put("nianxin", data.get("data1"));
			break;
		case 7:
			break;
		case 8:
			break;
		case 9:
			break;
		default:
			break;
		}
	}

	public void getShareReward(String id) {
		String uid = KeyUtils.uid(id);
		BoundHashOperations<String, String, String> ops = template
				.boundHashOps(uid);
		if (ops.get("canShare").equals("true")) {
			wealthService.getFlower(id, 5);
			ops.put("canShare", "false");
		}
	}

}
